load("@aio_npm//@angular-devkit/architect-cli:index.bzl", "architect", "architect_test")
load("@build_bazel_rules_nodejs//:index.bzl", "copy_to_bin", "npm_package_bin")
load("//tools:defaults.bzl", "nodejs_binary")
load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory")
load(":local_packages_util.bzl", "link_local_packages", "substitute_local_package_deps")
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag", "string_flag")
load("//aio/scripts:local_server_test.bzl", "local_server_test")
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
load(":aio_targets.bzl", "aio_test")
load("@bazel_skylib//lib:collections.bzl", "collections")

exports_files([
    "firebase.json",
    "ngsw-config.json",
    "ngsw-config.template.json",
    "tsconfig.json",
])

# If set will use first party angular deps for aio targets instead of their npm equivalent
bool_flag(
    name = "flag_aio_local_deps",
    build_setting_default = False,
)

config_setting(
    name = "aio_local_deps",
    flag_values = {
        ":flag_aio_local_deps": "true",
    },
)

# Config setting controlling the AIO architect configuration to build with
string_flag(
    name = "flag_aio_build_config",
    build_setting_default = "stable",
    values = [
        "stable",
        "rc",
        "next",
        "archive",
    ],
)

config_setting(
    name = "aio_build_config_stable",
    flag_values = {
        ":flag_aio_build_config": "stable",
    },
)

config_setting(
    name = "aio_build_config_rc",
    flag_values = {
        ":flag_aio_build_config": "rc",
    },
)

config_setting(
    name = "aio_build_config_next",
    flag_values = {
        ":flag_aio_build_config": "next",
    },
)

config_setting(
    name = "aio_build_config_archive",
    flag_values = {
        ":flag_aio_build_config": "archive",
    },
)

# Run dgeni generation
npm_package_bin(
    name = "dgeni",
    # angular.io-package is a js_library target, which copies sources to $(BINDIR)
    args = ["./$(BINDIR)/aio/tools/transforms/angular.io-package"],
    data = [
        "//aio/tools/transforms/angular.io-package",
        # Dgeni typescript package crawls type definitions for Angular dependencies
        "@npm//rxjs",
        "@npm//@types/node",
    ],
    env = {
        "BAZEL_DGENI_OUTPUT_PATH": "$(@D)",
        "GIT_BIN": "$(GIT_BIN_PATH)",
    },
    exec_properties = ENABLE_NETWORK,
    output_dir = True,
    stamp = "@rules_nodejs//nodejs/stamp:use_stamp_flag",
    # Makes remote git calls to get previous versions
    tags = [
        "requires-network",
    ],
    tool = "@aio_npm//dgeni/bin:dgeni",
    toolchains = [
        "@npm//@angular/build-tooling/bazel/git-toolchain:current_git_toolchain",
    ],
    visibility = ["//aio/tests/deployment:__subpackages__"],
)

# Copy stackblitz live examples to expected assets location
copy_to_directory(
    name = "stackblitz",
    srcs = ["//aio/content/examples:all-stackblitz"],
    replace_prefixes = {
        "examples": "generated/live-examples",
    },
    root_paths = [
        "aio/content",
    ],
)

# Copy example zips to expected assets location
copy_to_directory(
    name = "example-zips",
    srcs = ["//aio/content/examples:all-example-zips"],
    replace_prefixes = {
        "examples": "generated/zips",
    },
    root_paths = [
        "aio/content",
    ],
)

# All source and configuration files required to build the docs app
APPLICATION_FILES = [
    "angular.json",
    "tsconfig.app.json",
    "tsconfig.json",
    "tsconfig.worker.json",
    "security-exemptions.json",
    "//aio/src/assets",
    "//aio/src/assets/js",
] + glob(
    ["src/**/*"],
    exclude = [
        "src/**/*.spec.ts",
        # Temporarily exclude generated sources produced by the non-bazel
        # build until the whole project is built by bazel and this directory
        # isn't needed.
        "src/generated/**/*",
    ],
)

# Dependencies required to build the docs app.
APPLICATION_DEPS = [
    ":dgeni",
    ":stackblitz",
    ":example-zips",
    "//aio/src/generated:ngsw-config",
    "@aio_npm//@angular-devkit/build-angular",
    "@aio_npm//@angular-eslint/builder",
    "@aio_npm//@angular/animations",
    "@aio_npm//@angular/cdk",
    "@aio_npm//@angular/cli",
    "@aio_npm//@angular/common",
    "@aio_npm//@angular/compiler",
    "@aio_npm//@angular/compiler-cli",
    "@aio_npm//@angular/core",
    "@aio_npm//@angular/elements",
    "@aio_npm//@angular/forms",
    "@aio_npm//@angular/material",
    "@aio_npm//@angular/platform-browser",
    "@aio_npm//@angular/platform-browser-dynamic",
    "@aio_npm//@angular/router",
    "@aio_npm//@angular/service-worker",
    "@aio_npm//@types/lunr",
    "@aio_npm//@types/trusted-types",
    "@aio_npm//lunr",
    "@aio_npm//rxjs",
    "@aio_npm//safevalues",
    "@aio_npm//tslib",
    "@aio_npm//zone.js",
]

# All sources, specs, and config files required to test the docs app
TEST_FILES = APPLICATION_FILES + [
    "karma.conf.js",
    "tsconfig.spec.json",
] + glob(
    ["src/**/*.spec.ts"],
)

# Dependencies required to test the docs app
TEST_DEPS = APPLICATION_DEPS + [
    "@aio_npm//@angular/build-tooling/bazel/browsers/chromium",
    "@aio_npm//@types/jasmine",
    "@aio_npm//@types/node",
    "@aio_npm//assert",
    "@aio_npm//jasmine",
    "@aio_npm//jasmine-core",
    "@aio_npm//karma-chrome-launcher",
    "@aio_npm//karma-coverage",
    "@aio_npm//karma-jasmine",
    "@aio_npm//karma-jasmine-html-reporter",
    "//aio/tools:windows-chromium-path",
]

# All sources, specs, and config files required to serve the app
# and run e2e tests against it
E2E_FILES = APPLICATION_FILES + glob(["tests/e2e/**"])

# Dependencies required to run the e2e tests
E2E_DEPS = APPLICATION_DEPS + [
    "@aio_npm//@angular/build-tooling/bazel/browsers/chromium",
    "@aio_npm//@types/jasmine",
    "@aio_npm//@types/node",
    "@aio_npm//jasmine-spec-reporter",
    "@aio_npm//protractor",
    "@aio_npm//ts-node",
    "//aio/tools:windows-chromium-path",
]

# Poll period for architect rules that watch for changes
ARCHITECT_POLL_MS = 1000

# Stamp npm_link targets for all dependencies that correspond to a
# first-party equivalent pacakge in angular.
link_local_packages(all_aio_deps = collections.uniq(APPLICATION_DEPS + TEST_DEPS + E2E_DEPS))

copy_to_bin(
    name = "application_files_bin",
    srcs = APPLICATION_FILES,
)

architect(
    name = "build-app",
    args = select({
        ":aio_build_config_stable": ["site:build:stable"],
        ":aio_build_config_next": ["site:build:next"],
        ":aio_build_config_rc": ["site:build:rc"],
        ":aio_build_config_archive": ["site:build:archive"],
    }) + [
        "--output-path=build-app",
    ],
    chdir = "$(RULEDIR)",
    data = [":application_files_bin"] + select({
        ":aio_local_deps": substitute_local_package_deps(APPLICATION_DEPS),
        "//conditions:default": APPLICATION_DEPS,
    }),
    output_dir = True,
    # When building with local packages (--config=aio_local_deps), RBE complains about
    # the input tree being too large (> 70,000 files).
    tags = ["no-remote-exec"],
)

npm_package_bin(
    name = "404-page",
    outs = ["404.html"],
    args = [
        "$(execpath src/404-body.html)",
        "$(execpath :build-app)",
        "$@",
    ],
    data = [
        "src/404-body.html",
        ":build-app",
    ],
    tool = "//aio/scripts:build-404-page",
)

# Prepare the final build artifact which substitutes a generated 404 page
# into the architect output.
copy_to_directory(
    name = "build",
    srcs = [
        ":404-page",
        ":build-app",
    ],
    replace_prefixes = {
        "build-app": "",
    },
    visibility = ["//aio/scripts/deploy-to-firebase:__pkg__"],
)

TEST_DATA = TEST_FILES + select({
    ":aio_local_deps": substitute_local_package_deps(TEST_DEPS),
    "//conditions:default": TEST_DEPS,
})

aio_test(
    name = "test",
    args = [
        "site:test",
        "--no-watch",
    ],
    data = TEST_DATA,
)

aio_test(
    name = "test-and-watch",
    args = [
        "site:test",
        "--watch",
        "--poll=%d" % ARCHITECT_POLL_MS,
    ],
    data = TEST_DATA,
    tags = [
        "ibazel_notify_changes",
        "manual",
    ],
)

architect_test(
    name = "e2e",
    size = "large",
    args = [
        "site:e2e",
        "--no-webdriver-update",
        "--port=0",
    ],
    chdir = package_name(),
    data = E2E_FILES + select({
        ":aio_local_deps": substitute_local_package_deps(E2E_DEPS),
        "//conditions:default": E2E_DEPS,
    }),
    env = {
        "CHROME_BIN": "../$(CHROMIUM)",
        "CHROMEDRIVER_BIN": "../$(CHROMEDRIVER)",
    },
    # TODO: investigate why those tests became more flaky
    #       (presumably after AIO migration to Bazel)
    flaky = True,
    toolchains = [
        "@aio_npm//@angular/build-tooling/bazel/browsers/chromium:toolchain_alias",
    ],
)

# Build and serve the app. Run with ibazel to watch for changes and re-serve.
architect(
    name = "serve",
    args = [
        "site:serve",
        "--open",
        "--poll=%d" % ARCHITECT_POLL_MS,
        "--live-reload",
        "--watch",
    ],
    chdir = package_name(),
    data = APPLICATION_FILES + select({
        ":aio_local_deps": substitute_local_package_deps(APPLICATION_DEPS),
        "//conditions:default": APPLICATION_DEPS,
    }),
    tags = ["ibazel_notify_changes"],
)

# Build and serve the app, watch for changes, and run a fast but low-fidelity
# rebuild when docs change. Watching and serving is a part of the node script,
# so there is no need to run with ibazel, which would be slow as it would redo
# the full dgeni build on each change.

nodejs_binary(
    name = "docs-watch",
    chdir = package_name(),
    data = APPLICATION_FILES + [
        "//aio/scripts:fast-serve-and-watch",
    ] + select({
        ":aio_local_deps": substitute_local_package_deps(APPLICATION_DEPS),
        "//conditions:default": APPLICATION_DEPS,
    }),
    enable_linker = True,
    entry_point = "//aio/scripts:fast-serve-and-watch.js",
    env = {
        # Have the authors package output its low-fi dgeni build
        # to a different directory in the runfiles tree because bazel
        # write-protects original dgeni runfiles. Then in angular.json
        # we list this folder last under assets so that it will
        # take precedence over the original dgeni assets, effectively
        # overwriting select sources and then serving them.
        "BAZEL_DGENI_OUTPUT_PATH": "dgeni-fast",
    },
)

local_server_test(
    name = "test-a11y-score-localhost",
    args = ["LOCALHOST_URL"],
    entry_point = "//aio/scripts:test-aio-a11y",
    serve_target = ":build",
)

local_server_test(
    name = "test-pwa-score-localhost",
    args = [
        "LOCALHOST_URL",
        "all:0,pwa:100",
    ],
    data = [
        "//aio/scripts:audit-web-app",
    ],
    entry_point = "//aio/scripts:audit-web-app-script",
    serve_target = ":build",
)
